#include "stdafx.h"
#include "EventUtil.h"

class EventReg
{
public:
	static EventInfo*& GetHead()
	{
		static EventInfo* head = NULL;
		return head;
	}

	static EventInfo* Add(EventInfo* e)
	{
		uint len = strlen(e->GetName());
		if (len < 6 || strcmp(e->GetName() + len - 5, "Event") != 0)
		{
			//ASSERT_MSG(false, "'%s' : Event name should be end with 'Event'", e->GetName());
		}

		EventInfo* head = GetHead();
		EventInfo* next = head;
		head = e;
		return next;
	}
};

//////////////////////////////////////////////////////////////////////////

uint EventInfo::g_NumEventInfos = 0;
EventInfo** EventInfo::g_EventInfoArray = NULL;

EventInfo::EventInfo(const char* name, uint serialID, uint numBytes, EventFactoryProc* factoryProc)
:m_Name(name), m_ScriptMethodName(NULL)
{
	m_SerialID = serialID;
	m_NumBytes = numBytes;
	m_FactoryProc  = factoryProc;

	m_NextReg = EventReg::Add(this);
	m_RuntimeID = g_NumEventInfos++; // Assign new runtime ID
}

const EventInfo* EventInfo::FindByName(const char* name)
{
	for (EventInfo* e = EventReg::GetHead(); e; e = e->m_NextReg)
	{
		if (strcmp(e->m_Name, name) == 0)
			return e;
	}

	return NULL;
}

const EventInfo* EventInfo::FindByPtr(const void* ptr)
{
	if (ptr == NULL)
		return NULL;

	for (EventInfo* e = EventReg::GetHead(); e; e->m_NextReg)
	{
		if (e == ptr)
			return e;
	}

	return NULL;
}

const EventInfo* EventInfo::FindBySerialID(uint serialID)
{
	for (EventInfo* e = EventReg::GetHead(); e; e->m_NextReg)
	{
		if (e->m_SerialID != 0 && e->m_SerialID == serialID)
			return e;
	}

	return NULL;
}

const EventInfo* EventInfo::GetByRuntimeID(uint runtimeID)
{
	if (g_EventInfoArray == NULL)
	{
		// Create Array sorted by runtimeID
		g_EventInfoArray = new EventInfo* [g_NumEventInfos];

		uint count = 0;
		for (EventInfo* e = EventReg::GetHead(); e; e = e->m_NextReg)
		{
			g_EventInfoArray[count++] = e;
		}

		ASSERT(count == g_NumEventInfos);

		LessRuntimeID pred;
		std::sort(g_EventInfoArray, g_EventInfoArray + count, pred);

		ASSERT(g_EventInfoArray[0]->m_RuntimeID == 0);
		ASSERT(g_EventInfoArray[count-1]->m_RuntimeID == count-1);
	}

	return runtimeID < g_NumEventInfos ? g_EventInfoArray[runtimeID] : NULL;
}

const char* EventInfo::CreateScriptMethodName() const
{
	ASSERT(m_ScriptMethodName == NULL);

	char fnName[64];
	sprintf(fnName, "On%s", m_Name);	// Shrink 'OnXXXEvent' to 'OnXXX'
	fnName[strlen(m_Name) + 2 - 5] = 0;
	
	m_ScriptMethodName = _strdup(fnName);

	return m_ScriptMethodName;
}

void EventInfo::DestoryStaticData()
{
	SAFE_DELETE_ARRAY(g_EventInfoArray)

	for (EventInfo* e = EventReg::GetHead(); e; e->m_NextReg)
	{
		delete e->m_ScriptMethodName;
	}
}

//////////////////////////////////////////////////////////////////////////

EventChannel::EventChannel()
{
	m_IsSystemChannel = false;
	m_Scheduler = new EventScheduler(this);
}

EventChannel::~EventChannel()
{
	delete m_Scheduler;
}

void EventChannel::AddHandler(IEventHandler* handler, EventHandlerID serialID)
{
	ASSERT(handler);

	HandlerMap::iterator itr = m_HandlerMap.find(handler);

	if (itr != m_HandlerMap.end())
	{
		itr->second = serialID;
	}
	else
	{
		m_HandlerMap.insert(HandlerMap::value_type(handler, serialID));
	}
}

EventHandlerID EventChannel::GetHandlerID(IEventHandler* handler)
{
	HandlerMap::iterator itr = m_HandlerMap.find(handler);

	return itr != m_HandlerMap.end() ? itr->second : 0;
}

void EventChannel::RemoveHandler(IEventHandler* handler)
{
	HandlerMap::iterator itr = m_HandlerMap.find(handler);

	if (itr != m_HandlerMap.end())
	{
		m_HandlerMap.erase(itr);
		m_Scheduler->CancleFor(handler);
	}
}

bool EventChannel::Contains(IEventHandler* handler)
{
	return m_HandlerMap.find(handler) != m_HandlerMap.end();
}

void EventChannel::Send(IEventHandler* to, Event* evt)
{
	ASSERT(Contains(to));
	if (to->IsEnabled())
		to->OnEvent(evt);
}

struct EventChannel::BroadcastFunc
{
	Event* evt;
	BroadcastFunc(Event* evt) : evt(evt) {}
	void operator() (HandlerMap::value_type& val)
	{
		IEventHandler* handler = val.first;
		if (handler->IsEnabled())
			handler->OnEvent(evt);
	}
};

void EventChannel::ScheduleAfter(uint tickAfter, Event* evt, IEventHandler* to)
{
	if (to)
		ASSERT(Contains(to));
	
	m_Scheduler->ScheduleAfter(tickAfter, evt, to);
}

void EventChannel::Broadcast(Event* evt)
{
	// TODO : Improve this
	SafeIterate(m_HandlerMap, BroadcastFunc(evt));
}